package com.zeta.plan.controller;

import com.zeta.plan.exception.AuthenticationException;
import com.zeta.plan.exception.UsernameOrPasswordException;
import com.zeta.plan.model.ResponseResult;
import com.zeta.plan.model.dto.UserDTO;
import com.zeta.plan.model.entity.UserDO;
import com.zeta.plan.model.query.UserQuery;
import com.zeta.plan.service.UserService;
import com.zeta.plan.utils.JWTUtil;
import com.zeta.plan.utils.MD5Util;
import com.zeta.plan.utils.RandomString;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@RestController()
@RequestMapping("/api")
public class UserController {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    @Autowired
    private UserService userService;

    /**
     * 登录
     * @param userQuery
     * @param response
     * @return
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public ResponseResult<Void> login(@RequestBody UserQuery userQuery, HttpServletResponse response) {
        // 查询salt
        String salt = userService.findSaltByUsername(userQuery.getUsername());
        // 密码加密
        String password = MD5Util.md5(userQuery.getPassword(), salt, MD5Util.count);
        // 查询用户信息
        UserDO userDOInfo = userService.findUserByUsernameAndPassword(userQuery.getUsername(), password);
        if (userDOInfo == null) {
            throw new AuthenticationException("用户名或密码错误");
        }
        // 判断用户是否被注销
        if (!userDOInfo.getEnabled()) {
            throw new AuthenticationException("账户已被注销");
        }
        // 生成jwt签名密钥 secret
        String secret = RandomString.createRandomString(7);
        // 将 secret 存到数据库中
        userService.updateSecretByID(userDOInfo.getId(), secret);
        userDOInfo.setSecret(secret);
        // 更新redis中的secret
        redisTemplate.opsForValue().set(userDOInfo.getUsername() + ":secret", secret, JWTUtil.EXPIRE_TIME, TimeUnit.MILLISECONDS);
        // 封装payLoad
        Map<String, String> payloadMap = new HashMap<>();
        Long createdTime = System.currentTimeMillis();
        payloadMap.put("username", userDOInfo.getUsername());
        payloadMap.put("createdTime", createdTime.toString());
        // jwt 生成token
        String token = JWTUtil.createToken(createdTime,payloadMap, userDOInfo.getSecret());
        // 将refreshToken存入redis
        redisTemplate.opsForValue().set(userDOInfo.getUsername() + ":refreshToken", createdTime, JWTUtil.REFRESH_EXPIRE_TIME, TimeUnit.MINUTES);
        // 将新的token放入响应头中
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        httpServletResponse.setHeader("Authorization", token);
        httpServletResponse.setHeader("Access-Control-Expose-Headers", "Authorization");
        return ResponseResult.ok();
    }

    /**
     * 登出
     * 通过删除redis中的用户对应的refreshToken来表示登出
     * @param username
     * @return
     */
    @RequestMapping(value = "/logout", method = RequestMethod.POST)
    public ResponseResult<Void> logout(@RequestParam("username") String username) {
        if (redisTemplate.delete(username + ":refreshToken")) {
            return ResponseResult.ok();
        }
        return ResponseResult.error("用户已退出");
    }

    /**
     * 注销
     * 通过设置enabled为false来实现
     * @return
     */
    @RequestMapping(value = "/logoff",method = RequestMethod.DELETE)
    public ResponseResult<Void> logoff(@RequestParam("username") String username) {
        if (userService.updateEnabledByUsername(username, false)) {
            return ResponseResult.ok();
        }
        return ResponseResult.error("注销失败");
    }

    /**
     * 注册
     * @param userDO
     * @return
     */
    @RequestMapping(value = "/register", method = RequestMethod.PUT)
    public ResponseResult<Void> register(@RequestBody UserDO userDO) {
        if (!StringUtils.hasLength(userDO.getUsername()) || !StringUtils.hasLength(userDO.getPassword())) {
            throw new UsernameOrPasswordException("用户名或密码不能不空");
        }
        // 生成随机盐
        String salt = RandomString.createRandomString(6);
        // 加密
        String password = MD5Util.md5(userDO.getPassword(), salt, MD5Util.count);
        // 封装userDO
        userDO.setPassword(password)
                .setEnabled(true)
                .setCreatedDate(new Date())
                .setSalt(salt);

        if (userService.addUser(userDO) > 0) {
            return ResponseResult.ok();
        }
        return ResponseResult.error();

    }

    /**
     * 获取用户信息
     * @param id
     * @return
     */
    @RequestMapping(value = "/users/{id}",method = RequestMethod.GET)
    public ResponseResult<UserDO> getUserInfo(@PathVariable Long id) {
        UserDO userDOInfo = userService.findUserById(id);
        return ResponseResult.ok(userDOInfo);
    }

    /**
     * 修改密码
     * @param userQuery
     * @return
     */
    @RequestMapping(value = "/users/password", method = RequestMethod.PUT)
    public ResponseResult<Void> updatePassword(@RequestBody UserQuery userQuery) {
        // 密码加密处理
        String salt = userService.findSaltByUsername(userQuery.getUsername());
        userQuery.setPassword(MD5Util.md5(userQuery.getPassword(), salt, MD5Util.count));
        userQuery.setNewPassword(MD5Util.md5(userQuery.getNewPassword(), salt, MD5Util.count));

        // 更新密码
        userService.updatePasswordByUsernameAndPassword(userQuery.getUsername(), userQuery.getPassword(), userQuery.getNewPassword());
        // 删除redis中的 refreshToken（让用户重新登录认证）
        redisTemplate.delete(userQuery.getUsername() + ":refreshToken");
        return ResponseResult.ok();
    }

    // 密码找回


    /**
     * 更新用户信息
     * @param userDTO
     * @return
     */
    @RequestMapping(value = "/users/{id}", method =RequestMethod.POST)
    public ResponseResult<Void> updateUserInfo(@RequestBody UserDTO userDTO) {
        userService.updateUserInfoById(userDTO);
        return ResponseResult.ok();
    }







}
